{
  "cells": [
    {
      "cell_type": "raw",
      "metadata": {
        "vscode": {
          "languageId": "raw"
        }
      },
      "source": [
        "---\n",
        "title: Death of a value\n",
        "sidebar_position: 3\n",
        "description: An explanation of when and how Mojo destroys values.\n",
        "---"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "As soon as a value/object is no longer used, Mojo destroys it. Mojo does _not_\n",
        "wait until the end of a code block—or even until the end of an expression—to\n",
        "destroy an unused value. It destroys values using an “as soon as possible”\n",
        "(ASAP) destruction policy that runs after every sub-expression. Even within an\n",
        "expression like `a+b+c+d`, Mojo destroys the intermediate values as soon as\n",
        "they're no longer needed.\n",
        "\n",
        "Mojo uses static compiler analysis to find the point where a value is last used.\n",
        "Then, Mojo immediately ends the value's lifetime and calls the `__del__()`\n",
        "destructor to perform any necessary cleanup for the type. \n",
        "\n",
        "For example, notice when the `__del__()` destructor is called for each instance\n",
        "of `MyPet`:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Loki\n",
            "Destruct Loki\n",
            "Destruct Charlie\n",
            "Sylvie\n",
            "Destruct Sylvie\n"
          ]
        }
      ],
      "source": [
        "@value\n",
        "struct MyPet:\n",
        "    var name: String\n",
        "    var age: Int\n",
        "\n",
        "    fn __del__(owned self):\n",
        "        print(\"Destruct\", self.name)\n",
        "\n",
        "fn pets():\n",
        "    var a = MyPet(\"Loki\", 4)\n",
        "    var b = MyPet(\"Sylvie\", 2)\n",
        "    print(a.name)\n",
        "    # a.__del__() runs here for \"Loki\"\n",
        "\n",
        "    a = MyPet(\"Charlie\", 8)\n",
        "    # a.__del__() runs immediately because \"Charlie\" is never used\n",
        "\n",
        "    print(b.name)\n",
        "    # b.__del__() runs here\n",
        "\n",
        "pets()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Notice that each initialization of a value is matched with a call to the\n",
        "destructor, and `a` is actually destroyed multiple times—once for each time it receives\n",
        "a new value.\n",
        "\n",
        "Also notice that this `__del__()` implementation doesn't actually do\n",
        "anything. Most structs don't require a custom destructor, and Mojo automatically\n",
        "adds a no-op destructor if you don't define one.\n",
        "\n",
        "### Default destruction behavior\n",
        "\n",
        "You may be wondering how Mojo can destroy a type without a custom destructor, or\n",
        "why a no-op destructor is useful. If a type is simply a collection of fields,\n",
        "like the `MyPet` example, Mojo only needs to destroy the fields: `MyPet` doesn't\n",
        "dynamically allocate memory or use any long-lived resources (like file handles).\n",
        "There's no special action to take when a `MyPet` value is destroyed.\n",
        "\n",
        "Looking at the individual fields, `MyPet` includes an `Int` and a `String`. The\n",
        "`Int` is what Mojo calls a _trivial type_. It's a statically-sized bundle of \n",
        "bits. Mojo knows exactly how big it is, so those bits can be reused to store\n",
        "something else.\n",
        "\n",
        "The `String` value is a little more complicated. Mojo strings are mutable. The\n",
        "`String` object has an internal buffer—a\n",
        "[`List`](/mojo/stdlib/collections/list/List) field,\n",
        "which holds the characters that make up the string. A `List` stores\n",
        "its contents in dynamically allocated memory on the heap, so the string can\n",
        "grow or shrink. The string itself doesn't have any special destructor logic,\n",
        "but when Mojo destroys a string, it calls the destructor for the\n",
        "`List` field, which de-allocates the memory.\n",
        "\n",
        "Since `String` and `Int` don't require any custom destructor logic, they both\n",
        "have no-op destructors: literally, `__del__()` methods that don't do anything.\n",
        "This may seem pointless, but it means that Mojo can call the destructor on any\n",
        "value when its lifetime ends. This makes it easier to write generic containers\n",
        "and algorithms.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### Benefits of ASAP destruction\n",
        "\n",
        "Similar to other languages, Mojo follows the principle that objects/values\n",
        "acquire resources in a constructor (`__init__()`) and release resources in a\n",
        "destructor (`__del__()`). However, Mojo's ASAP destruction has some advantages\n",
        "over scope-based destruction (such as the C++ [RAII\n",
        "pattern](https://en.cppreference.com/w/cpp/language/raii), which waits until\n",
        "the end of the code scope to destroy values):\n",
        "\n",
        "- Destroying values immediately at last-use composes nicely with the \"move\"\n",
        "  optimization, which transforms a \"copy+del\" pair into a \"move\" operation.\n",
        "\n",
        "- Destroying values at end-of-scope in C++ is problematic for some common\n",
        "  patterns like tail recursion, because the destructor call happens after the\n",
        "  tail call. This can be a significant performance and memory problem for\n",
        "  certain functional programming patterns, which is not a problem in Mojo,\n",
        "  because the destructor call always happens before the tail call.\n",
        "\n",
        "Additionally, Mojo's ASAP destruction works great within Python-style `def`\n",
        "functions. That's because Python doesn’t really provide scopes beyond a\n",
        "function scope, so the Python garbage collector cleans up resources more often\n",
        "than a scope-based destruction policy would. However, Mojo does not use a\n",
        "garbage collector, so the ASAP destruction policy provides destruction\n",
        "guarantees that are even more fine-grained than in Python.\n",
        "\n",
        "The Mojo destruction policy is more similar to how Rust and Swift work, because\n",
        "they both have strong value ownership tracking and provide memory safety. One\n",
        "difference is that Rust and Swift require the use of a [dynamic \"drop\n",
        "flag\"](https://doc.rust-lang.org/nomicon/drop-flags.html)—they maintain hidden\n",
        "shadow variables to keep track of the state of your values to provide safety.\n",
        "These are often optimized away, but the Mojo approach eliminates this overhead\n",
        "entirely, making the generated code faster and avoiding ambiguity."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Destructor\n",
        "\n",
        "Mojo calls a value's destructor (`__del__()` method) when the value's lifetime\n",
        "ends (typically the point at which the value is last used). As we mentioned\n",
        "earlier, Mojo provides a default, no-op destructor for all types, so in most\n",
        "cases you don't need to define the `__del__()` method.\n",
        "\n",
        "You should define the `__del__()` method to perform any kind of cleanup the\n",
        "type requires. Usually, that includes freeing memory for any fields where you\n",
        "dynamically allocated memory (for example, via `Pointer` or `DTypePointer`) and\n",
        "closing any long-lived resources such as file handles.\n",
        "\n",
        "However, any struct that is just a simple collection of other types does not\n",
        "need to implement the destructor.\n",
        "\n",
        "For example, consider this simple struct:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "metadata": {},
      "outputs": [],
      "source": [
        "struct MyPet:\n",
        "    var name: String\n",
        "    var age: Int\n",
        "\n",
        "    fn __init__(inout self, name: String, age: Int):\n",
        "        self.name = name\n",
        "        self.age = age"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "There's no need to define the `__del__()` destructor for this, because it's a\n",
        "simple collection of other types (`String` and `Int`), and it doesn't \n",
        "dynamically allocate memory. \n",
        "\n",
        "Whereas, the following struct must define the `__del__()` method to free the\n",
        "memory allocated by its\n",
        "[`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer):"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "from memory.unsafe_pointer import UnsafePointer, initialize_pointee_copy, destroy_pointee\n",
        "\n",
        "struct HeapArray:\n",
        "    var data: UnsafePointer[Int]\n",
        "    var size: Int\n",
        "\n",
        "    fn __init__(inout self, size: Int, val: Int):\n",
        "        self.size = size\n",
        "        self.data = UnsafePointer[Int].alloc(self.size)\n",
        "        for i in range(self.size):\n",
        "            initialize_pointee_copy(self.data + i, val)\n",
        "\n",
        "    fn __del__(owned self):\n",
        "        for i in range(self.size):\n",
        "            destroy_pointee(self.data + i)\n",
        "        self.data.free()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Note that a pointer doesn't _own_ any values in the memory it points to, so\n",
        "when a pointer is destroyed, Mojo doesn't call the destructors on those values.\n",
        "\n",
        "So in the `HeapArray` example above, calling `free()` on the pointer releases\n",
        "the memory, but doesn't call the destructors on the stored values. To invoke\n",
        "the destructors, use the `destroy_pointee()` function from the  \n",
        "`unsafe_pointer` module.\n",
        "\n",
        ":::note\n",
        "\n",
        "You can't just call the destructor explicitly. Because `__del__()`\n",
        "takes `self` as an `owned` value, and owned arguments are copied by default,\n",
        "`foo.__del__()` actually creates and destroys a _copy_ of `foo`. When Mojo\n",
        "destroys a value, however, it passes in the original value as `self`, not a\n",
        "copy.\n",
        "\n",
        ":::"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n",
        "It's important to notice that the `__del__()` method is an \"extra\" cleanup\n",
        "event, and your implementation does not override any default destruction\n",
        "behaviors. For example, Mojo still destroys all the fields in `MyPet` even\n",
        "if you implement `__del__()` to do nothing:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "metadata": {},
      "outputs": [],
      "source": [
        "struct MyPet:\n",
        "    var name: String\n",
        "    var age: Int\n",
        "\n",
        "    fn __init__(inout self, name: String, age: Int):\n",
        "        self.name = name\n",
        "        self.age = age\n",
        "\n",
        "    fn __del__(owned self):\n",
        "        # Mojo destroys all the fields when they're last used\n",
        "        pass"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "However, the `self` value inside the `__del__()` destructor is still whole (so\n",
        "all fields are still usable) until the destructor returns, as we'll discuss\n",
        "more in the following section."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Field lifetimes\n",
        "\n",
        "In addition to tracking the lifetime of all objects in a program, Mojo also\n",
        "tracks each field of a structure independently. That is, Mojo keeps track of\n",
        "whether a \"whole object\" is fully or partially initialized/destroyed, and it\n",
        "destroys each field independently with its ASAP destruction policy.\n",
        "\n",
        "For example, consider this code that changes the value of a field:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "metadata": {},
      "outputs": [],
      "source": [
        "@value\n",
        "struct MyPet:\n",
        "    var name: String\n",
        "    var age: Int\n",
        "\n",
        "fn use_two_strings():\n",
        "    var pet = MyPet(\"Po\", 8)\n",
        "    print(pet.name)\n",
        "    # pet.name.__del__() runs here, because this instance is\n",
        "    # no longer used; it's replaced below\n",
        "\n",
        "    pet.name = String(\"Lola\") # Overwrite pet.name\n",
        "    print(pet.name)\n",
        "    # pet.__del__() runs here"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "The `pet.name` field is destroyed after the first `print()`, because Mojo knows\n",
        "that it will be overwritten below. You can also see this behavior when using the\n",
        "transfer operator:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "metadata": {},
      "outputs": [],
      "source": [
        "fn consume(owned arg: String):\n",
        "    pass\n",
        "\n",
        "fn use(arg: MyPet):\n",
        "    print(arg.name)\n",
        "\n",
        "fn consume_and_use():\n",
        "    var pet = MyPet(\"Selma\", 5)\n",
        "    consume(pet.name^)\n",
        "    # pet.name.__moveinit__() runs here, which destroys pet.name\n",
        "    # Now pet is only partially initialized\n",
        "\n",
        "    # use(pet)  # This fails because pet.name is uninitialized\n",
        "\n",
        "    pet.name = String(\"Jasper\")  # All together now\n",
        "    use(pet)                     # This is ok\n",
        "    # pet.__del__() runs here (and only if the object is whole)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Notice that the code transfers ownership of the `name` field to `consume()`.\n",
        "For a period of time after that, the `name` field is uninitialized.\n",
        "Then `name` is reinitialized before it is passed to the `use()` function. If you\n",
        "try calling `use()` before `name` is re-initialized, Mojo rejects the code\n",
        "with an uninitialized field error.\n",
        "\n",
        "Also, if you don't re-initialize the name by the end of the `pet` lifetime, the\n",
        "compiler complains because it's unable to destroy a partially initialized\n",
        "object.\n",
        "\n",
        "Mojo's policy here is powerful and intentionally straight-forward: fields can\n",
        "be temporarily transferred, but the \"whole object\" must be constructed with the\n",
        "aggregate type’s initializer and destroyed with the aggregate destructor. This\n",
        "means it's impossible to create an object by initializing only its fields, and\n",
        "it's likewise impossible to destroy an object by destroying only its fields."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### Field lifetimes during destruct and move\n",
        "\n",
        "The consuming-move constructor and destructor face an interesting situation\n",
        "with field lifetimes, because, unlike other lifecycle methods, they both take\n",
        "an instance of their own type as an `owned` argument, which is about to be\n",
        "destroyed. You don't really need to worry about this detail when implementing\n",
        "these methods, but it might help you better understand field lifetimes.\n",
        "\n",
        "Just to recap, the move constructor and destructor method signatures\n",
        "look like this:\n",
        "\n",
        "```mojo\n",
        "struct TwoStrings:\n",
        "    fn __moveinit__(inout self, owned existing: Self):\n",
        "        # Initializes a new `self` by consuming the contents of `existing`\n",
        "    fn __del__(owned self):\n",
        "        # Destroys all resources in `self`\n",
        "```\n",
        "\n",
        ":::note\n",
        "\n",
        "There are two kinds of \"self\" here: capitalized `Self` is an alias\n",
        "for the current type name (used as a type specifier for the `existing`\n",
        "argument), whereas lowercase `self` is the argument name for the\n",
        "implicitly-passed reference to the current instance (also called \"this\" in\n",
        "other languages, and also implicitly a `Self` type).\n",
        "\n",
        ":::\n",
        "\n",
        "Both of these methods face an interesting but obscure problem: they both must\n",
        "dismantle the `existing`/`self` value that's `owned`. That is, `__moveinit__()`\n",
        "implicitly destroys sub-elements of `existing` in order to transfer ownership\n",
        "to a new instance (read more about the [move\n",
        "constructor](/mojo/manual/lifecycle/life#move-constructor)),\n",
        "while `__del__()` implements the deletion logic for its `self`. As such, they\n",
        "both need to own and transform elements of the `owned` value, and they\n",
        "definitely don’t want the original `owned` value's destructor to also run—that\n",
        "could result in a double-free error, and in the case of the `__del__()` method,\n",
        "it would become an infinite loop.\n",
        "\n",
        "To solve this problem, Mojo handles these two methods specially by assuming\n",
        "that their whole values are destroyed upon reaching any return from the method.\n",
        "This means that the whole object may be used as usual, up until the field\n",
        "values are transferred or the method returns.\n",
        "\n",
        "For example, the following code works as you would expect (within the\n",
        "destructor, we can still pass ownership of a field value to another function,\n",
        "and there's no infinite loop to destroy `self`):"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "metadata": {},
      "outputs": [],
      "source": [
        "fn consume(owned str: String):\n",
        "    print('Consumed', str)\n",
        "\n",
        "struct TwoStrings:\n",
        "    var str1: String\n",
        "    var str2: String\n",
        "\n",
        "    fn __init__(inout self, one: String):\n",
        "        self.str1 = one\n",
        "        self.str2 = String(\"bar\")\n",
        "\n",
        "    fn __moveinit__(inout self, owned existing: Self):\n",
        "        self.str1 = existing.str1\n",
        "        self.str2 = existing.str2\n",
        "\n",
        "    fn __del__(owned self):\n",
        "        self.dump() # Self is still whole here\n",
        "        # Mojo calls self.str2.__del__() since str2 isn't used anymore\n",
        "\n",
        "        consume(self.str1^)\n",
        "        # self.str1 has been transferred so it is also destroyed now;\n",
        "        # `self.__del__()` is not called (avoiding an infinite loop).\n",
        "\n",
        "    fn dump(inout self):\n",
        "        print('str1:', self.str1)\n",
        "        print('str2:', self.str2)\n",
        "\n",
        "fn use_two_strings():\n",
        "    var two_strings = TwoStrings(\"foo\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Explicit lifetimes\n",
        "\n",
        "So far, we've described how Mojo destroys a value at the point it's last used,\n",
        "and this works great in almost all situations. However, there are very rare\n",
        "situations in which Mojo simply cannot predict this correctly and will destroy\n",
        "a value that is still referenced through some other means.\n",
        "\n",
        "For instance, perhaps you're building a type with a field that carries a pointer\n",
        "to another field. The Mojo compiler won't be able to reason about the pointer,\n",
        "so it might destroy a field (`obj1`) when that field is technically no longer\n",
        "used, even though another field (`obj2`) still holds a pointer to part of it.\n",
        "So, you might need to keep `obj1` alive until you can execute some special\n",
        "logic in the destructor or move initializer.\n",
        "\n",
        "You can force Mojo to keep a value alive up to a certain point by assigning the\n",
        "value to the `_` discard pattern at the point where it's okay to destroy it.\n",
        "For example:\n",
        "\n",
        "```mojo\n",
        "fn __del__(owned self):\n",
        "    self.dump() # Self is still whole here\n",
        "\n",
        "    consume(self.obj2^)\n",
        "    _ = self.obj1\n",
        "    # Mojo keeps `obj1` alive until here, after its \"last use\"\n",
        "```\n",
        "\n",
        "In this case, if `consume()` refers to some value in `obj1` somehow, this\n",
        "ensures that Mojo does not destroy `obj1` until after the call to `consume()`,\n",
        "because assignment to the discard variable `_` is actually the last use.\n",
        "\n",
        "For other situations, you can also scope the lifetime of a value using the\n",
        "Python-style [`with`\n",
        "statement](https://docs.python.org/3/reference/compound_stmts.html#the-with-statement).\n",
        "That is, for any value defined at the entrance to a `with` statement, Mojo will\n",
        "keep that value alive until the end of the `with` statement. For example:\n",
        "\n",
        "```mojo\n",
        "with open(\"my_file.txt\", \"r\") as file:\n",
        "    print(file.read())\n",
        "\n",
        "    # Other stuff happens here (whether using `file` or not)...\n",
        "    foo()\n",
        "    # `file` is alive up to the end of the `with` statement.\n",
        "\n",
        "# `file` is destroyed when the statement ends.\n",
        "bar()\n",
        "```"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Mojo",
      "language": "mojo",
      "name": "mojo-jupyter-kernel"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "mojo"
      },
      "file_extension": ".mojo",
      "mimetype": "text/x-mojo",
      "name": "mojo"
    },
    "orig_nbformat": 4
  },
  "nbformat": 4,
  "nbformat_minor": 2
}
